/*
	Consult Layer Functions

*/
#include "globals.h"
#include "layer_uart.h"
#include "layer_comm.h"
#include "layer_consult.h"

// ---------------------------------------------------------------------
// Typdefinitions
enum _eConMode
{
	eConUnkown = 0,
	eConStream = 1,
	eConBypass = 2
} _eConMode;

enum _eConRcvState
{
	eConRcvSearchHdr	=0x00,
	eConRcvSearchBytes	=0x01,
	eConRcvStreamBytes	=0x02,
	eConRcvSearchInit	=0x10,
	eConRcvReBeginSend	=0x11,
	eConRcvMin			=0x12,
	eConRcvMax			=0x30
} _eConRcvState;

typedef struct _typConsultVars
{
	enum _eConMode		eConMode;
	enum _eConRcvState	eConState;
	_consult_callback	consult_callback;
	uint8_t				chFirstByte;
	uint8_t				bytes_expected;
	uint8_t				bytes_status;
	uint32_t			bitset;
} _typConsultVars;

// ---------------------------------------------------------------------
// Macro's
#define CONSULT_TX_SIZE		0x0020
#define CONSULT_RX_SIZE		0x0024
#define CONSULT_BAUD		9600
#define CONSULT_UART		1
#define CONSULT_HEADER		0xFF
#define CONSULT_INIT_RESPONSE 0x10
#define CONSULT_END_CHAR	0xF0
#define CONSULT_REQUEST_CMD	0x5A
#define CONSULT_CMD_FAIL	0xFE
#define CONSULT_REQUEST_MAX 0x10

// Define the functions to use explicitley
#define uart_enable_tx(bEnable)				uart_enable_tx_1 (bEnable)
#define uart_enable_rx(bEnable)				uart_enable_rx_1 (bEnable)
#define uart_send_byte_unbuffered(data)		uart_send_byte_unbuffered_1 (data)
#define uart_get_byte()						uart_get_byte_1 ()
// ---------------------------------------------------------------------
// Static Varialbes
static _typConsultVars	typConVars;
static unsigned char consult_tx_data[CONSULT_TX_SIZE];	//Ring Buffer
static unsigned char consult_rx_data[CONSULT_RX_SIZE];	//Always from zero

// ---------------------------------------------------------------------
// Private Functions
void	consult_item_nullify	(uint8_t position);
uint8_t consult_item_byte		(uint8_t position);
void	consult_recieve			(unsigned char c, u16 nLength);

// ---------------------------------------------------------------------
// Function Bodies
// ---------------------------------------------------------------------

// Initialise the consult uart and buffers
void	consult_init		(_consult_callback consult_callback, uint32_t	bitset)
{
	uart_init(CONSULT_UART, CONSULT_BAUD, &consult_recieve,
			consult_tx_data, CONSULT_TX_SIZE,
			consult_rx_data, CONSULT_RX_SIZE,
			eIntNone);

	typConVars.bitset = bitset;
	typConVars.eConMode = eConUnkown;
	typConVars.consult_callback = consult_callback;
}

// Initialise the consult communications
void	consult_init_comm	(void)
{
	uart_flush_rx_buffer(CONSULT_UART);
	uart_bytes_rx(CONSULT_UART,1);

	if (typConVars.eConState != eConRcvStreamBytes 
	|| typConVars.eConMode != eConStream )
	{
		uint8_t cmd[3] = {0xFF,0xFF,0xEF};

		typConVars.eConMode = eConStream;
		typConVars.eConState = eConRcvSearchInit;

		// Send Initialise String
		uart_send_buffer(CONSULT_UART, cmd, 3);
	}
}

void	consult_stop_comm	(void)
{
	uint8_t	chStop[] = {0x30};
	uart_send_buffer(CONSULT_UART, chStop, 1);
}

void	consult_init_bypass	(uint8_t FirstByte, uint8_t Length)
{
	uart_flush_rx_buffer(CONSULT_UART);
	uart_bytes_rx(CONSULT_UART,1);

	typConVars.eConMode = eConBypass;
	typConVars.eConState = eConRcvSearchBytes;

	typConVars.chFirstByte = FirstByte;
	typConVars.bytes_expected = Length;
}

// En/Disable the consult Rx
void	consult_enable		(uint8_t bEnable)
{
	uart_enable_rx( bEnable);
}

// Retrieve a status of the current communications
uint8_t		consult_get_status	(void)
{
	return typConVars.eConState >= eConRcvMin ? 1 : 0;
}

// Retrieves the status count number and resets it to zero
uint8_t		consult_check_status(void)
{
	uint8_t	bSts = typConVars.bytes_status;
	typConVars.bytes_status = 0;
	return bSts;
}

// Recieve the consult data as requested
void consult_recieve		(unsigned char c, u16 nLength)
{
//	register unsigned char c = consult_rx_data[nLength-1];

	typConVars.bytes_status = 0x01;

	switch (typConVars.eConMode)
	{
	case eConStream:

		switch (typConVars.eConState)
		{
		case eConRcvSearchHdr:
			if (c == CONSULT_HEADER)
			{ 
				typConVars.eConState = eConRcvSearchBytes;
				uart_bytes_rx(CONSULT_UART,2);
			} 
			else
			{
				uart_bytes_rx(CONSULT_UART,1);
				uart_flush_rx_buffer(CONSULT_UART); 
			}
			break;
		case eConRcvSearchBytes:
			if (c == typConVars.bytes_expected)
			{ 
				typConVars.eConState = eConRcvStreamBytes;
				uart_bytes_rx(CONSULT_UART, typConVars.bytes_expected + 4);
			} 
			else
			{
				typConVars.eConState = eConRcvSearchHdr;
				uart_flush_rx_buffer(CONSULT_UART); 
				uart_bytes_rx(CONSULT_UART,1);
			}
			break;

		case eConRcvStreamBytes:
			if (consult_rx_data[0] == 0xFF 
			 && consult_rx_data[1] == typConVars.bytes_expected
			 && consult_rx_data[typConVars.bytes_expected+2] == 0xFF 
			 && consult_rx_data[typConVars.bytes_expected+3] == typConVars.bytes_expected)
			{
				unsigned char append[] = {0xFF , typConVars.bytes_expected};
				
				// Do the consult shite here
				typConVars.consult_callback(consult_rx_data, typConVars.bytes_expected+2, typConVars.bitset);
				
				// RE-ADD THE BUFFER AND CONTINUE
				uart_flush_rx_buffer(CONSULT_UART); 
				uart_append_buffer_rx(CONSULT_UART, append, 2);
			}
			else
			{
				typConVars.eConState = eConRcvSearchHdr;
				uart_flush_rx_buffer(CONSULT_UART); 
				uart_bytes_rx(CONSULT_UART,1);
			}

			break;

		case eConRcvSearchInit:

			uart_bytes_rx(CONSULT_UART,1);

			if (c == CONSULT_INIT_RESPONSE)
			{
				unsigned char cmd[] = {CONSULT_REQUEST_CMD,0};

				if ((cmd[1] = consult_item_byte(0)) != 0xFF)
				{
					uart_send_buffer(CONSULT_UART, cmd, 2);
					typConVars.bytes_expected = 1;
					typConVars.eConState = eConRcvMin;
				}
			}
			else
			{
				typConVars.eConState = eConRcvSearchInit;
				uart_flush_rx_buffer(CONSULT_UART); 
			}
			break;

		case eConRcvReBeginSend:
			uart_bytes_rx(CONSULT_UART,1);

			{
				unsigned char cmd[] = {CONSULT_REQUEST_CMD,0};

				if ((cmd[1] = consult_item_byte(0)) != 0xFF)
				{
					uart_send_buffer(CONSULT_UART, cmd, 2);
					typConVars.bytes_expected = 1;
					typConVars.eConState = eConRcvMin;
				}
			}

			break;

		default:
			if (typConVars.eConState >= eConRcvMin
			 && typConVars.eConState <= eConRcvMax)
			{
				if ( c == CONSULT_CMD_FAIL )
				{
					consult_item_nullify(typConVars.eConState - eConRcvMin);
					typConVars.eConState = eConRcvReBeginSend;
				}
				else if ( c != 0xA5 )
				{
					unsigned char cmd[] = {CONSULT_REQUEST_CMD,0};

					if ((cmd[1] = consult_item_byte(typConVars.eConState - eConRcvMin)) != 0xFF
					&& typConVars.bytes_expected < CONSULT_REQUEST_MAX)
					{
						uart_send_buffer(CONSULT_UART, cmd, 2);
						typConVars.bytes_expected++;
						typConVars.eConState++;
					}
					else
					{
						for (c=0;c<0x10;c++)
						{
							consult_item_nullify(CONSULT_REQUEST_MAX);
						}
						cmd[0] = CONSULT_END_CHAR;
						uart_send_buffer(CONSULT_UART, cmd, 1);
						typConVars.eConState = eConRcvSearchHdr;
						uart_bytes_rx(CONSULT_UART,1);
					}
				}
			}
			else
			{
				typConVars.eConState = eConRcvSearchInit;
				uart_flush_rx_buffer(CONSULT_UART); 
				uart_bytes_rx(CONSULT_UART,1);
			}

			break;
		}

		break;

	case eConBypass:

		switch (typConVars.eConState)
		{
		case eConRcvSearchBytes:
			if (c == typConVars.chFirstByte)
			{
				typConVars.eConState = eConRcvStreamBytes;
				uart_bytes_rx(CONSULT_UART, typConVars.bytes_expected);

				if (typConVars.bytes_expected != 1)
				{
					break;
				}
			}
			else
			{
				uart_flush_rx_buffer(CONSULT_UART); 
				uart_bytes_rx(CONSULT_UART,1);
				break;
			}

			// Intentional fall through

		case eConRcvStreamBytes:
			{
				// Create the new message
				comm_new( eMsgPc, eMsgEcu, typConVars.bytes_expected);

				// Append the payload
				comm_append(consult_rx_data,typConVars.bytes_expected);

				// Send the buffer
				comm_send();

				// Recieved
				typConVars.eConState = eConRcvSearchBytes;			
				uart_flush_rx_buffer(CONSULT_UART); 
				uart_bytes_rx(CONSULT_UART,1);
			}

			break;

		default:
			break;
		}

		break;
	default:
		break;
	}
}


// Consult Byte functions
void consult_item_nullify	(uint8_t position)
{
	uint16_t i;
	uint16_t byCount = 0;

	for(i=0;i<32;i++)
	{
		if ((typConVars.bitset >> i) & (uint32_t)0x00000001)
		{
			byCount++;
		}
		if ((byCount-1) == position)
		{
			typConVars.bitset &= ((uint32_t)0xFFFFFFFF ^ ((uint32_t)0x00000001 << i));
			return;
		}
	}
}

// Gets the byte for the consult item given a position
uint8_t consult_item_byte		(uint8_t position)
{
	uint16_t i;
	uint16_t byCount = 0;

	for(i=0;i<32;i++)
	{
		if ((typConVars.bitset >> i) & (uint32_t)0x01)
		{
			byCount++;
		}
		if ((byCount-1) == position)
		{
			return i;
		}
	}
	return 0xFF;
}
uint8_t	consult_test_RxClk		(void)
{
	int nCh;
	cli();

	nCh = uart_get_byte();

	sei();

	if (nCh == 0x00)
		return 0x01;
	else
		return 0x00;
}

uint8_t	consult_test_RxTx		(void)
{
	int		nCh;
	cli();
	
	uart_send_byte_unbuffered( 0xEF);
	nCh = uart_get_byte();

	sei();

	if (nCh == 0x08)
		return 0x01;
	else
		return 0x00;
}
